﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;

namespace Brain
{
    /// <summary>
    /// Class for Animating Vectors2
    /// </summary>
    public class Animation_Vector2 : IBaseAnimation
    {
        /// <summary>
        /// All the Frames this Animation class have
        /// </summary>
        public KeyFrame_Vector2[] Frames { get; set; }

        /// <summary>
        /// If the animation should Loop
        /// </summary>
        public bool Loop { get; set; }

        private int currentFrame;
        /// <summary>
        /// The Current Frame this Animation is at.
        /// Be aware that setting this value WILL Update the
        /// Animation
        /// </summary>
        public int CurrentFrame
        {
            get { return currentFrame; }
            set
            {
                currentFrame = value;
                Update();
            }
        }

        /// <summary>
        /// The Length of the Animation. Can change if the Animation is looping, due
        /// to the Loop Restart Time
        /// </summary>
        public int AnimationEnd
        {
            get 
            {
                if (Loop)
                {
                    return totalTime + LoopRestartTime;
                }
                else
                {
                    return totalTime;
                }
            }
        }

        /// <summary>
        /// The Frame the Animation should start
        /// </summary>
        public int StartFrame { get; set; }

        /// <summary>
        /// A delegate used to set the Vector2 value
        /// to the owner
        /// </summary>
        public SetVector2 Set { get; set; }

        /// <summary>
        /// The Time it takes for the Loop to restart
        /// </summary>
        public int LoopRestartTime = 50;

        /// <summary>
        /// Creates a new Animation_Vector2. Constructor used for Deserializing ONLY, use the another
        /// </summary>
        public Animation_Vector2()
        {
            ShouldSerialize = true;
        }
        /// <summary>
        /// Creates a new Animation_Vector2
        /// </summary>
        public Animation_Vector2(SetVector2 Set)
        {
            this.Set = Set;
            ShouldSerialize = true;
        }

        private List<KeyFrame_Vector2> frames;

        private int totalTime;
        public void SubmitFrame(int time, Vector2 value)
        {
            if (frames == null)
            {
                frames = new List<KeyFrame_Vector2>();
            }
            if (time > totalTime)
            {
                totalTime = time;
            }
            KeyFrame_Vector2 frame = new KeyFrame_Vector2();
            frame.State = value;
            frame.Time = time;

            this.frames.Add(frame);

            isOptimized = false;
        }

        private bool isOptimized = false;
        /// <summary>
        /// If True, the Array of Frames is ordered by time,
        /// and thus the Array of Frames is made, not the list
        /// </summary>
        public bool IsOptimized
        {
            get { return isOptimized; }
        }

        public void Optimize()
        {
            this.frames.Sort(CompareKeyFrames);
            this.Frames = frames.ToArray();

            isOptimized = true;
        }
        private int CompareKeyFrames(KeyFrame_Vector2 frame1, KeyFrame_Vector2 frame2)
        {
            return frame1.Time.CompareTo(frame2.Time);
        }

        private Vector2 finalValue;
        public Vector2 FinalValue
        {
            get
            {
                return finalValue;
            }
            set
            {
                finalValue = value;
                if (Set != null)
                {
                    Set(value);
                }
            }
        }

        private void Update()
        {
            if (Frames == null || !isOptimized)
            {
                Optimize();
            }

            bool isNull;
            int index;
            KeyFrame_Vector2 Frame = FindLastKeyFrame(CurrentFrame, out isNull, out index);

            if (!isNull)
            {
                bool nextNull;
                bool isRestart;
                KeyFrame_Vector2 Next = FindNextFrame(index, out nextNull, out isRestart);

                if (nextNull)
                {
                    // Nothing to Interpolate, just set the Position to the Frame pos
                    this.FinalValue = Frame.State;
                }
                else
                {
                    if (isRestart)
                    {
                        float amount = ((float)CurrentFrame - Frame.Time) / (float)LoopRestartTime;
                        this.FinalValue = Frame.Lerp(Frame.State, Next.State, amount);
                    }
                    else
                    {
                        float amount = ((float)CurrentFrame - Next.Time) / ((float)Frame.Time - Next.Time);
                        this.FinalValue = Next.Lerp(Next.State, Frame.State, amount);
                    }
                }
            }
        }

        private float currentLoopFrame;
        public void UpdateLoop(float frameDif)
        {
            currentLoopFrame += frameDif;

            while (currentLoopFrame > this.AnimationEnd)
            {
                currentLoopFrame -= this.AnimationEnd;
            }

            int current = (int)currentLoopFrame;
            if (currentFrame != current)
            {
                this.CurrentFrame = current;
            }
        }
        public void Update(int frame)
        {
            this.CurrentFrame = frame;
        }

        protected KeyFrame_Vector2 FindLastKeyFrame(int nextTime, out bool isNull, out int index)
        {
            object lastFoundFrame = null;
            KeyFrame_Vector2 lastFoundF = default(KeyFrame_Vector2);

            isNull = true;
            index = -1;

            for (int i = 0; i < Frames.Length; i++)
            {
                KeyFrame_Vector2 fr = Frames[i];
                if (lastFoundFrame == null)
                {
                    if (fr.Time < nextTime)
                    {
                        index = i;
                        lastFoundF = fr;
                        lastFoundFrame = lastFoundF;

                        isNull = false;
                    }
                }
                else
                {
                    if (fr.Time <= nextTime &&
                        fr.Time > lastFoundF.Time)
                    {
                        index = i;
                        lastFoundF = fr;
                        lastFoundFrame = fr;
                    }
                }
            }


            return lastFoundF;
        }
        protected KeyFrame_Vector2 FindNextFrame(int index, out bool isNull, out bool isRestart)
        {
            int nextIndex = index + 1;
            if (this.Frames.Length > nextIndex)
            {
                isNull = false;
                isRestart = false;
                return this.Frames[nextIndex];
            }

            if (Loop)
            {
                // Pick the first frame
                isNull = false;
                isRestart = true;
                return this.Frames[0];
            }

            isNull = true;
            isRestart = false;
            return default(KeyFrame_Vector2);
        }

        public bool ShouldSerialize { get; set; }

        public void SerializeData(SerializationData parentData, string add)
        {
            if (Frames == null | !isOptimized)
            {
                Optimize();
            }

            parentData.AddData("Anim_Vec2.Frames", this.Frames.Length, add);
            for (int i = 0; i < this.Frames.Length; i++)
            {
                KeyFrame_Vector2 frame = this.Frames[i];
                parentData.AddData("Anim_Vec2.FrameTime" + i, frame.Time, add);
                parentData.AddData("Anim_Vec2.FrameState" + i, frame.State, add);
            }
        }

        public void DeserializeData(SerializationData parentData, string add)
        {
            int frames = parentData.GetData<int>("Anim_Vec2.Frames", add);
            for (int i = 0; i < frames; i++)
            {
                int time = parentData.GetData<int>("Anim_Vec2.FrameTime" + i, add);
                Vector2 state = parentData.GetData<Vector2>("Anim_Vec2.FrameState" + i, add);
                SubmitFrame(time, state);
            }

            Optimize();
        }


        public void DeserializedAll()
        {
        }
    }
}
